//
// Created by chizuru on 2025/9/3.
//

#include "BOBCATCameraService.h"
#include "RegisterReadCommand.h"
#include <unordered_map>
using std::optional;

BOBCATCameraService::BOBCATCameraService(CameraDevice& c, std::shared_ptr<TinyXmlRegisterCLBMap> rm, RetryPolicy rp)
    : CameraController(c, rm, rp){

}

BOBCATCameraService::~BOBCATCameraService() {

}


OperationResult BOBCATCameraService::checkIfCameraConnected() {
    std::optional<uint32_t> regVal;
    auto r = getRegister("CameraInformation", "TestRegister", regVal);
    if (!r.success) {
        return r;
    }

    if (*regVal != 0x76543210) {
        return OperationResult::Fail(ErrorCode::ReadFailure, "No camera");
    }
    return OperationResult::Ok("Connected");
}

OperationResult BOBCATCameraService::setCameraContinuesMode() {
    return applyTransactionWrite({
        {"Trigger", "TriggeringDisable", 1},
        {"ExposureControl", "Prog.FrameTimeEnable", 0},
        {"ExposureControl", "ExposureControlMode", 2}
    });
}

OperationResult BOBCATCameraService::setCameraDoubleExposureMode() {
    return applyTransactionWrite({
        {"ExposureControl", "Prog.FrameTimeEnable", 0},
        {"Trigger", "TriggeringDisable", 0},
        {"Trigger", "TriggeringModeSelector", 2},
        {"Trigger", "TriggerInputSelector", 1},
        {"Trigger", "TriggerEdgeSelector", 0},
        {"Trigger", "TriggerOverlap", 0},
        {"Trigger", "De-bounceTimeSelector", 1},
        {"ExposureControl", "ExposureControlMode", 2}
    });
}

OperationResult BOBCATCameraService::setCameraExternalTriggerMode() {
    return applyTransactionWrite({
        {"ExposureControl", "Prog.FrameTimeEnable", 0},
        {"Trigger", "TriggeringDisable", 0},
        {"Trigger", "TriggeringModeSelector", 1},
        {"Trigger", "TriggerInputSelector", 1},
        {"Trigger", "TriggerEdgeSelector", 0},
        {"Trigger", "TriggerOverlap", 0},
        {"Trigger", "De-bounceTimeSelector", 1},
        {"ExposureControl", "ExposureControlMode", 2}
    });
}

OperationResult BOBCATCameraService::setCameraExternalControlMode() {
    return applyTransactionWrite({
         {"ExposureControl", "Prog.FrameTimeEnable", 0},
         {"Trigger", "TriggeringDisable", 0},
         {"Trigger", "TriggeringModeSelector", 0},
         {"Trigger", "TriggerInputSelector", 1},
         {"Trigger", "TriggerEdgeSelector", 0},
         {"Trigger", "De-bounceTimeSelector", 1},
         {"ExposureControl", "ExposureControlMode", 1}
    });
}

OperationResult BOBCATCameraService::setCameraFrequencyControlMode() {
    return applyTransactionWrite({
         {"Trigger", "TriggeringDisable", 1},
         {"ExposureControl", "Prog.FrameTimeEnable", 1},
         {"ExposureControl", "ExposureControlMode", 2}
    });
}

OperationResult BOBCATCameraService::setCameraSpeed(uint32_t value) {
    return CameraController::setRegister("CameraControl", "CameraSpeed", value);
}

OperationResult BOBCATCameraService::setCameraBitDepth(uint32_t value) {
    return CameraController::setRegister("OutputDataFormat", "BitDepthSelector", value);
}

OperationResult BOBCATCameraService::setCameraGain(uint32_t value) {
    return applyTransactionWrite({{"Video","AnalogGainCh.1",value},
         {"Video","AnalogGainCh.2",value},
         {"Video","AnalogGainCh.3",value},
         {"Video","AnalogGainCh.4",value}});
}

OperationResult BOBCATCameraService::setExposureTime(uint32_t value) {
    return CameraController::setRegister("ExposureControl", "ExposureTime", value);
}

OperationResult BOBCATCameraService::setFrameRate(uint32_t value) {
    return CameraController::setRegister("ExposureControl", "FrameTime", value);
}

OperationResult BOBCATCameraService::getCurrentMaxExposureTime(uint32_t& value) {
    optional<uint32_t> regVal;
    auto r = getRegister("CameraInformation", "CurrentMaximumExposure", regVal);
    if (!r.success) {
        return r;
    }
    value = *regVal;
    return r;
}

OperationResult BOBCATCameraService::getCurrentMinExposureTime(uint32_t& value) {
    optional<uint32_t> regVal;
    auto r = getRegister("CameraInformation", "CurrentMinimumExposure", regVal);
    if (!r.success) {
        return r;
    }
    value = *regVal;
    return r;
}

OperationResult BOBCATCameraService::setTriggerControl(uint32_t value) {
    return CameraController::setRegister("Trigger", "TriggeringEnable", value);
}

OperationResult BOBCATCameraService::setFrameTimeEnable(uint32_t value) {
    return CameraController::setRegister("ExposureControl", "FrameTime", value);
}

OperationResult BOBCATCameraService::setExposureControlMode(uint32_t value) {
    return CameraController::setRegister("ExposureControl", "ExposureControlMode", value);
}

OperationResult BOBCATCameraService::setTriggerInputSelector(uint32_t value) {
    return CameraController::setRegister("Trigger", "TriggerInputSelector", value);
}

OperationResult BOBCATCameraService::setTriggerEdgeSelector(uint32_t value) {
    return CameraController::setRegister("Trigger", "TriggerEdgeSelector", value);
}

OperationResult BOBCATCameraService::setTriggerOverlap(uint32_t value) {
    return CameraController::setRegister("Trigger", "TriggerOverlap", value);
}

OperationResult BOBCATCameraService::setDebounceTimeSelector(uint32_t value) {
    return CameraController::setRegister("Trigger", "De-bounceTimeSelector", value);
}

OperationResult BOBCATCameraService::getCameraType(std::string& value) {
    std::vector<std::optional<uint32_t>> regValues;
    auto r = applyTransactionRead({
        {"ManufacturingData", "CameraType1"},
        {"ManufacturingData", "CameraType2"},
        {"ManufacturingData", "CameraType3"},
        {"ManufacturingData", "CameraType4"}},
     regValues);

    if (!r.success) {
        return r;
    }

    std::string type;
    for (int i = 0; i < regValues.size(); i++) {
        type += CLBProtocolCodec::parseUint32ToString(*regValues[i]);
    }
    value = type;

    return r;
}

OperationResult BOBCATCameraService::getSerialNumber(std::string &value) {
    std::vector<std::optional<uint32_t>> regValues;
    auto r = applyTransactionRead({
        {"ManufacturingData", "AssemblySerialNumber1"},
        {"ManufacturingData", "AssemblySerialNumber2"}},
     regValues);
    if (!r.success) {
        return r;
    }

    std::string numStr;
    for (int i = 0; i < regValues.size(); i++) {
        numStr += CLBProtocolCodec::parseUint32ToString(*regValues[i]);
    }
    value = numStr;

    return r;
}


static std::unordered_map<std::string, std::string> regHashTable = {
        {"BootFrom", "SavingAndRestoring"},
        {"LoadFromFactory", "SavingAndRestoring"},
        {"RS-232BaudRateSelector", "SavingAndRestoring"},
        {"TestRegister", "CameraInformation"},
        {"SoftReset", "CameraInformation"},
        {"HorizontalFrameSize", "CameraInformation"},
        {"VerticalFrameSize ", "CameraInformation"},
        {"CurrentMinimumFrameTime", "CameraInformation"},
        {"CurrentMaximumExposure", "CameraInformation"},
        {"CurrentMinimumExposure", "CameraInformation"},
        {"ExposureControlMode", "ExposureControl"},
        {"ExposureTime", "ExposureControl"},
        {"Prog.FrameTimeEnable", "ExposureControl"},
        {"FrameTime", "ExposureControl"},
        {"AnalogGainCh.1", "Video"},
        {"AnalogGainCh.2", "Video"},
        {"AnalogGainCh.3", "Video"},
        {"AnalogGainCh.4", "Video"},
        {"TriggerInputSelector", "Trigger"},
        {"TriggerEdgeSelector", "Trigger"},
        {"De-bounceTimeSelector", "Trigger"},
        {"TriggerOverlap", "Trigger"},
        {"TriggeringModeSelector", "Trigger"},
        {"TriggeringDisable", "Trigger"},
        {"BitDepthSelector", "OutputDataFormat"},
        {"TapModeSelector", "OutputDataFormat"},
        {"DataFormatSelector ", "OutputDataFormat"},
        {"CameraSpeed", "OutputDataFormat"},
        {"AssemblySerialNumber1", "ManufacturingData"},
        {"AssemblySerialNumber2", "ManufacturingData"},
        {"CameraType1", "ManufacturingData"},
        {"CameraType2", "ManufacturingData"},
        {"CameraType3", "ManufacturingData"},
        {"CameraType4", "ManufacturingData"}
};
OperationResult BOBCATCameraService::getRegisterCommand(const std::string& regName) {
    std::optional<uint32_t> regVal;
    if (regHashTable.find(regName) == regHashTable.end()) {
        return OperationResult::Fail(ErrorCode::InvalidArgument, "Not Such Register");
    }
    auto ok = CameraController::getRegister(regHashTable[regName], regName, regVal);
    if (!ok.success) {
        return ok;
    }
    std::cout << std::hex << std::setw(8) << std::setfill('0') << *regVal << std::endl;
    return OperationResult::Ok();
}

OperationResult BOBCATCameraService::setRegisterCommand(const std::string& regName, uint32_t val) {
    std::optional<uint32_t> regVal;
    if (regHashTable.find(regName) == regHashTable.end()) {
        return OperationResult::Fail(ErrorCode::InvalidArgument, "Not Such Register");
    }
    auto ok = CameraController::setRegister(regHashTable[regName], regName, val);
    if (!ok.success) {
        return ok;
    }
    return OperationResult::Ok();
}
